package cn.iocoder.yudao.module.reading.dal.mysql.punch;

import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.reading.dal.dataobject.punch.PunchRecordDO;
import org.apache.ibatis.annotations.Mapper;

import java.time.LocalDate;
import java.util.List;

/**
 * 学习打卡记录 Mapper
 *
 * @author 芋道源码
 */
@Mapper
public interface PunchRecordMapper extends BaseMapperX<PunchRecordDO> {

    /**
     * 根据用户ID和日期查询打卡记录
     *
     * @param userId    用户ID
     * @param punchDate 打卡日期
     * @return 打卡记录
     */
    default PunchRecordDO selectByUserIdAndDate(Long userId, LocalDate punchDate) {
        return selectOne(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId)
                .eq(PunchRecordDO::getPunchDate, punchDate));
    }

    /**
     * 根据用户ID查询打卡记录列表
     *
     * @param userId 用户ID
     * @param limit  限制数量
     * @return 打卡记录列表
     */
    default List<PunchRecordDO> selectByUserId(Long userId, Integer limit) {
        return selectList(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId)
                .orderByDesc(PunchRecordDO::getPunchDate)
                .last(limit != null ? "LIMIT " + limit : ""));
    }

    /**
     * 分页查询用户打卡记录列表
     *
     * @param userId    用户ID
     * @param pageParam 分页参数
     * @return 分页结果
     */
    default PageResult<PunchRecordDO> selectPageByUserId(Long userId, PageParam pageParam) {
        return selectPage(pageParam, new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId)
                .orderByDesc(PunchRecordDO::getPunchDate));
    }

    /**
     * 根据用户ID和日期范围查询打卡记录
     *
     * @param userId    用户ID
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 打卡记录列表
     */
    default List<PunchRecordDO> selectByUserIdAndDateRange(Long userId, LocalDate startDate, LocalDate endDate) {
        return selectList(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId)
                .between(PunchRecordDO::getPunchDate, startDate, endDate)
                .orderByAsc(PunchRecordDO::getPunchDate));
    }

    /**
     * 统计用户总打卡天数
     *
     * @param userId 用户ID
     * @return 总打卡天数
     */
    default Long countByUserId(Long userId) {
        return selectCount(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId));
    }

    /**
     * 查询用户最后一次打卡记录
     *
     * @param userId 用户ID
     * @return 最后一次打卡记录
     */
    default PunchRecordDO selectLastByUserId(Long userId) {
        return selectOne(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId)
                .orderByDesc(PunchRecordDO::getPunchDate)
                .last("LIMIT 1"));
    }

    /**
     * 查询公开的打卡记录（用于社区展示）
     *
     * @param limit 限制数量
     * @return 公开打卡记录列表
     */
    default List<PunchRecordDO> selectPublicRecords(Integer limit) {
        return selectList(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getIsPublic, true)
                .orderByDesc(PunchRecordDO::getCreateTime)
                .last("LIMIT " + limit));
    }

    /**
     * 根据用户ID删除所有打卡记录
     *
     * @param userId 用户ID
     * @return 删除数量
     */
    default int deleteByUserId(Long userId) {
        return delete(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId));
    }

    /**
     * 查询公开的打卡记录（用于动态列表）
     *
     * @param limit 限制数量
     * @return 公开打卡记录列表
     */
    default List<PunchRecordDO> selectPublicPunchRecords(Integer limit) {
        return selectList(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getIsPublic, true)
                .orderByDesc(PunchRecordDO::getCreateTime)
                .last(limit != null ? "LIMIT " + limit : "LIMIT 20"));
    }

    /**
     * 分页查询公开的打卡记录（用于动态列表）
     *
     * @param pageParam 分页参数
     * @return 分页结果
     */
    default PageResult<PunchRecordDO> selectPublicPunchRecordsPage(PageParam pageParam) {
        return selectPage(pageParam, new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getIsPublic, true)
                .orderByDesc(PunchRecordDO::getCreateTime));
    }

    /**
     * 检查用户在指定日期是否已打卡
     *
     * @param userId    用户ID
     * @param punchDate 打卡日期
     * @return 是否已打卡
     */
    default boolean existsByUserIdAndDate(Long userId, LocalDate punchDate) {
        return selectCount(new LambdaQueryWrapperX<PunchRecordDO>()
                .eq(PunchRecordDO::getUserId, userId)
                .eq(PunchRecordDO::getPunchDate, punchDate)) > 0;
    }

    /**
     * 根据班级ID查询打卡记录（排除老师）
     * 包括两种情况：
     * 1. 直接关联班级的打卡记录（punch_records.class_id = classId）
     * 2. 班级成员的所有打卡记录（通过class_members表关联，排除角色为teacher的成员）
     *
     * @param classId 班级ID
     * @return 打卡记录列表
     */
    @org.apache.ibatis.annotations.Select("""
        SELECT DISTINCT pr.* FROM punch_records pr
        WHERE pr.deleted = 0
        AND (
            pr.class_id = #{classId}
            OR pr.user_id IN (
                SELECT cm.user_id FROM class_members cm
                WHERE cm.class_id = #{classId} AND cm.deleted = 0 AND cm.role != 'teacher'
            )
        )
        ORDER BY pr.create_time DESC
        """)
    List<PunchRecordDO> selectByClassId(Long classId);

    /**
     * 根据班级ID和日期范围查询打卡记录（排除老师）
     *
     * @param classId   班级ID
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 打卡记录列表
     */
    @org.apache.ibatis.annotations.Select("""
        SELECT DISTINCT pr.* FROM punch_records pr
        WHERE pr.deleted = 0
        AND pr.punch_date BETWEEN #{startDate} AND #{endDate}
        AND (
            pr.class_id = #{classId}
            OR pr.user_id IN (
                SELECT cm.user_id FROM class_members cm
                WHERE cm.class_id = #{classId} AND cm.deleted = 0 AND cm.role != 'teacher'
            )
        )
        ORDER BY pr.punch_date DESC, pr.create_time DESC
        """)
    List<PunchRecordDO> selectByClassIdAndDateRange(Long classId, LocalDate startDate, LocalDate endDate);

    /**
     * 统计班级打卡记录总数（排除老师）
     *
     * @param classId 班级ID
     * @return 打卡记录总数
     */
    @org.apache.ibatis.annotations.Select("""
        SELECT COUNT(DISTINCT pr.id) FROM punch_records pr
        WHERE pr.deleted = 0
        AND (
            pr.class_id = #{classId}
            OR pr.user_id IN (
                SELECT cm.user_id FROM class_members cm
                WHERE cm.class_id = #{classId} AND cm.deleted = 0 AND cm.role != 'teacher'
            )
        )
        """)
    Long countByClassId(Long classId);

}
